home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freaks Macintosh Archive
/
Freaks Macintosh Archive.bin
/
Freaks Macintosh Archives
/
PC
/
EDACS.ZIP
/
EDACS.C
next >
Wrap
C/C++ Source or Header
|
1997-12-28
|
28KB
|
759 lines
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
#include <math.h>
/* EDACS control channel monitoring program... */
/* Scanner / system specific stuff needed for "trunk tracking" mode */
static double baudrate=1200.0;/* baud rate of computer to scanner link*/
/* This is the table equating logical channel numbers with actual */
/* frequencies. The first entry, chan[0], gives the frequency for */
/* logical channel number 1; chan[1] give the frequency for LCN 2, */
/* et cetera. You will need to change this table to match those */
/* systems in your own area unless you happen to live in Huntsville */
/* Alabama and want to monitor this 5 channel commercial SMR trunk */
static double chan[30] = {
856.0375, 857.0375, 858.0375, 859.0375, 860.0375
};
/* this identifies the control channel in the above table */
int control = 1; /* this number is the lcn of the control channel - 1 */
int nucha = 20; /* number of channels in system; for the above */
/* system it should be set to five, but it was put to*/
/* 20 for people who don't want to change the default*/
/* global variables */
int lc=0;
int fobp=0; /* pointer to current position in array fob */
char ob[1000]; /* buffer for raw data frames */
int obp=0; /* pointer to current position in array ob */
int invfla = 0; /* bit inversion flag */
static int comport = 0x3f8; /* serial port base address; set in main*/
static int sport = 1; /* serial port number to use */
static int dotting = 0; /* dotting sequence detection flag */
static int mode = 0; /* mode flag; 0 means we're on control */
/* channel; 1 means active frequency */
/* array for raw data coming off the serial port */
static unsigned int buflen= 15000; /* length of data buffer */
static volatile unsigned int cpstn = 0; /* current position in buffer */
static volatile unsigned int fdata[15001] ; /* timing data array */
void interrupt (*oldfuncc) (); /* vector to old com port interrupt */
/*--------------------------------------------------------------------*/
/* A BUNCH OF LOW LEVEL STUFF FOLLOWS */
/*--------------------------------------------------------------------*/
/**********************************************************************/
/* comint */
/* */
/* this is serial com port interrupt */
/* we assume here that it only gets called when one of the status */
/* lines on the serial port changes (that's all you have hooked up). */
/* All this handler does is find the number of system timer ticks */
/* since the last call and stores it in the fdata array. The MSB */
/* is set to indicate whether the status line is zero. In this way */
/* the fdata array is continuously updated with the appropriate */
/* length and polarity of each data pulse for further processing by */
/* the main program. */
void interrupt comint()
{
static unsigned int d1,d2,ltick,tick,dtick;
/* the system timer is a 16 bit counter whose value counts down */
/* from 65535 to zero and repeats ad nauseum. For those who really */
/* care, every time the count reaches zero the system timer */
/* interrupt is called (remember that thing that gets called every */
/* 55 milliseconds and does housekeeping such as checking the */
/* keyboard. */
outportb (0x43, 0x00); /* latch counter until we read it */
d1 = inportb (0x40); /* get low count */
d2 = inportb (0x40); /* get high count */
/* get difference between current, last counter reading */
tick = (d2 << 8) + d1;
dtick = ltick - tick;
ltick = tick;
/* set MSB to reflect state of input line */
if ((inportb(comport + 6) & 0xF0) > 0) dtick = dtick | 0x8000;
else dtick = dtick & 0x3fff;
fdata[cpstn] = dtick; /* put freq in fdata array */
cpstn ++; /* increment data buffer pointer */
if (cpstn>buflen) cpstn=0; /* make sure cpstn doesnt leave array */
d1 = inportb (comport + 2); /* clear IIR */
d1 = inportb (comport + 5); /* clear LSR */
d1 = inportb (comport + 6); /* clear MSR */
d1 = inportb (comport); /* clear RX */
outportb (0x20, 0x20); /* this is the END OF INTERRUPT SIGNAL */
}
/************************************************************************/
/* SERIAL PORT INITIALIZATION */
/************************************************************************/
/* basic purpose: enable modem status interrupt and set serial port */
/* output lines to supply power to interface */
void set8250 (double bps) /* sets up the 8250 UART */
{
static unsigned int t,dv,tp;
dv = (int) 1843200.0/(16.0*bps);
/* how to configure your serial port setup: */
/* do you want two stop bits? then make sure you set bit 2 in tp */
/* do you want a parity bit generated? then set bit bit 3 in tp */
/* do you want even parity? then set bit 4 in tp */
/* do you want an 8 bit word length? set bits 0 and 1 in tp */
/* do you want an 7 bit word length? set bit 1 in tp */
tp = 0x80 + 0x02 + 0x01;
outportb (comport+3, tp); /* set line control register */
outport (comport , dv); /* output brg divisor latch */
tp = tp & 0x7f; /* switch out brg divisor reg */
outportb (comport+3, tp);
outportb (comport+1, 0x08); /* enable MODEM STATUS INTERRUPT */
outportb (comport+4, 0x0a); /* push up RTS, DOWN DTR */
t = inportb(comport + 5); /* clear LSR */
t = inportb(comport); /* clear RX */
t = inportb(comport + 6); /* clear MSR */
t = inportb(comport + 2); /* clear IID */
t = inportb(comport + 2); /* clear IID - again to make sure */
}
/* this routine allows the RTS output line to be set either high or */
/* low depending on whether sta = 0. */
void rts_state(int sta)
{
static int rv;
rv = inportb(comport + 4);
rv = rv & 0x01; /* save DTR state */
rv = rv | 0x08; /* enable interrupt to reach PIC */
if (sta != 0) rv = rv | 0x02;
outportb(comport + 4, rv);
}
/* this routine allows the DTR output line to be set either high or */
/* low depending on whether sta = 0. */
void dtr_state(int sta)
{
static int rv;
rv = inportb(comport + 4);
rv = rv & 0x02; /* save RTS state */
rv = rv | 0x08; /* make sure interrupts can reach PIC */
if (sta != 0) rv = rv | 0x01;
outportb(comport + 4, rv);
}
/************************************************************************/
/* send_char */
/* */
/* This routine sends a character out on the serial port TxD line. */
/* It makes sure the previous character has been completely sent and */
/* then sends puts the current character into the transmit register. */
/************************************************************************/
void send_char(int c)
{
/* wait for transmit reg to empty */
while ( (inportb(comport+0x05) & 0x20) == 0x00);
/* send character */
outportb(comport,c);
}
/************************************************************************/
/* set_freak */
/* */
/* This routine must be written by the user for his/her specific radio. */
/* The input is the desired frequency the scanner should go to. */
/* Characters are sent to the scanner using the send_char routine. */
/************************************************************************/
void set_freak(double freq)
{
/* put your code here */
/* call send_char() to send a byte to your scanner */
}
/************************************************************************/
/* TIMER CHIP INITIALIZATION */
/************************************************************************/
/* purpose: make sure computer timekeeper is set up properly. This */
/* routine probably isn't necessary - it's just an insurance */
/* policy. */
void set8253() /* set up the 8253 timer chip */
{ /* NOTE: ctr zero, the one we are using*/
/* is incremented every 840nSec, is */
/* main system time keeper for dos */
outportb (0x43, 0x34); /* set ctr 0 to mode 2, binary */
outportb (0x40, 0x00); /* this gives us the max count */
outportb (0x40, 0x00);
}
/**********************************************************************/
/* higher level stuff follows */
/**********************************************************************/
/* structure for storing the control channel data frame information */
struct ccdat{
int cmd; /* command */
int lcn; /* logical channel number */
int stat; /* status bits */
int id; /* agency/fleet/subfleet id */
int bad; /* CRC check flag - when set to 1 it means data is corrupt */
};
/* array acn[] indicates which channels are currently active; aid[] */
/* indicates which id is using the active channel */
/* acn[] is actually a countdown timer decremented each time a */
/* control channel command is received and is reset whenever that */
/* channel appears in control channel data stream. When this reaches */
/* zero the channel is assumed to have become inactive. */
/* aid[] is used to make sure a new group appearing on an active */
/* channel is recognized even if acn[] had not yet reached zero */
int acn[33],aid[33],nacn=0;
/************************************************************************/
/* show_setup */
/* */
/* Purpose: setup things for screen display */
/************************************************************************/
void show_setup()
{
static int i;
clrscr();
for (i=0; i<33; i++) { acn[i] = 0; aid[i] = 0xffff; }
gotoxy(1,1);
textcolor(YELLOW);
cprintf("LCN ID ST COMMAND");
gotoxy(1,2);
cprintf("--- ---- --- ---------");
textcolor(LIGHTGRAY);
gotoxy(65,2);
cprintf("Using COM%1i",sport);
gotoxy(63,3);
cprintf("# channels: %2i",nucha);
gotoxy(60,4);
cprintf("Exit: Space or Esc ");
gotoxy(60,6);
cprintf("Misc Control data");
gotoxy(60,7);
cprintf("LCN ID ST CMD");
gotoxy(75,1);
textcolor(WHITE);
cprintf("-");
nacn=0;
}
/************************************************************************/
/* show_active */
/* */
/* this routine gets called ONCE whenever a new ID becomes active on */
/* a given channel number */
/************************************************************************/
void show_active(struct ccdat info)
{
static int linx,liny,cm;
/* this is where you will want to insert some code that decides if */
/* you want to switch to this particular active frequency. Right */
/* now this commented out code fragment would have sent your scanner */
/* off to any active frequency. */
/* you'll probably also want to filter out data channel assignemnts */
/* set_freak(chan[info.lcn-1]); */
/* mode = 1; */
/* dotting = 0; */
linx = 1;
liny = 2 + info.lcn;
if (liny > 22)
{
linx = 31;
liny -= 20;
}
gotoxy(linx,liny);
cm = info.cmd;
cprintf("%2i: %03X %01X %02X ",info.lcn,info.id,info.stat,cm);
textcolor(LIGHTGRAY);
if (cm == 0xEE) cprintf ("VOICE");
else if (cm == 0xEC) cprintf ("PHONE");
else if (cm == 0xF6) cprintf ("VOICE");
else if (cm == 0xA0) cprintf ("DATA");
else if (cm == 0xA1) cprintf ("DATA");
textcolor(WHITE);
}
/************************************************************************/
/* show_inactive gets called when a channel is no longer active */
/* it simply writes out a bunch of spaces to erase the old */
/* information */
/************************************************************************/
void show_inactive(int lcn)
{
static int linx,liny;
linx = 4;
liny = 2 + lcn;
if (liny > 22)
{
linx = 34;
liny -= 20;
}
gotoxy(linx,liny);
aid[lcn] = 0xffff;
cprintf(" ");
}
/* scroll raw commands on window in right hand side of display */
void show_raw(struct ccdat inf)
{
static int lc=8;
if (lc == 24) movetext(60,9,79,24,60,8);
else lc++;
gotoxy(61,lc);
cprintf("%02X %03X %01X %02X",inf.lcn,inf.id,inf.stat,inf.cmd);
}
/************************************************************************/
/* proc_cmd */
/* */
/* This routine figures out when a group becomes active on a new */
/* channel and when a channel is has become inactive. This info is */
/* passed to the two routines above which show the changes on the */
/* screen */
/************************************************************************/
void show(struct ccdat info)
{
static int idup=0,cdup=0,i,aid[8],sysid=0x0000;
static char dup[4] = { 45 , 47 , 124 , 92};
/* update the "I'm still receiving data" spinwheel character on screen
*/ cdup++; if (cdup > 9) { gotoxy(75,1); idup = (idup + 1) & 0x03;
putch(dup[idup]); cdup = 0; }
/* process only good information blocks */
if (info.bad == 0)
{
/* try to display all non idle information blocks */
if (info.cmd < 0x80)
{
show_raw(info);
}
else if ( ((info.cmd >> 1) != 0x7E) )
{
if ( (info.lcn > 0) & (info.lcn <= nucha) )
{
/* check to see if ID is in active list... if not it must be new */
if ( aid[info.lcn] != info.id)
{
/* if (aid[info.lcn] != 0xffff) show_inactive(info.lcn); */
/* a new ID has become active */
show_active(info);
/* add to activity list */
aid[info.lcn] = info.id;
}
/* update activity timer for this active channel */
acn[info.lcn] = 8 + (nacn << 2);
}
}
else if (info.cmd == 0xFD)
{
/* look at background / idle stuff to find system id */
if (sysid != info.id)
{
sysid = info.id;
textcolor(YELLOW);
gotoxy(35,1);
cprintf("SYS ID: %03X",sysid);
textcolor(WHITE);
}
}
}
/* see if any channel numbers have dropped off */
/* this is done by waiting for a certain number of control channel */
/* commands to go by before assuming the channel is no longer */
/* active. */
nacn = 0; /* nacn holds the number of active channels */
for (i=1; i<=nucha; i++)
{
if (acn[i] != 0)
{
nacn++;
acn[i]--;
if (acn[i] == 0)
{
/* LCN has become inactive */
show_inactive(i);
aid[i] = 0xffff;
}
}
}
}
/************************************************************************/
/* proc_cmd */
/* */
/* This routine processes a data frame by checking the CRC and nicely */
/* formatting the resulting data into structure info */
/************************************************************************/
void proc_cmd(int c)
{
static int ecc=0,sre=0,cc=0,ud=0xA9C,nbb=0,tb,orf,i;
static char oub[50];
static struct ccdat info;
if (c < 0)
{
cc = 0;
sre = 0x7D7;
ecc = 0x000;
oub[28] = 0;
/* pick off, store command (eight bits) */
for (i=0; i<=7; i++)
{
orf = orf << 1;
orf += oub[i];
}
orf = orf & 0xff;
info.cmd = orf;
/* pick off LCN (five bits) */
for (i=8; i<=12; i++)
{
orf = orf << 1;
orf += oub[i];
}
orf = orf & 0x1f;
info.lcn = orf;
/* pick off four status bits */
for (i=13; i<=16; i++)
{
orf = orf << 1;
orf+=oub[i];
}
orf = orf & 0x0f;
info.stat = orf;
/* pick off 11 ID bits */
for (i=17; i<=27; i++)
{
orf = orf << 1;
orf+=oub[i];
}
orf = orf & 0x07ff;
info.id = orf;
if (nbb == 0) info.bad = 0; else info.bad = 1;
if (nbb == 0)
{
show(info);
}
nbb = 0;
}
else
{
cc++;
/* bits 1 through 28 will be run through crc routine */
if (cc <29)
{
oub[cc-1] = c;
if ( c == 1) ecc = ecc ^ sre;
if ( (sre & 0x01) > 0) sre = (sre >> 1) ^ ud; else sre = sre >> 1;
}
else
{
/* for the rest of the bits - check if they match calculated crc */
/* and keep track of the number of wrong bits in variable nbb */
if ( (ecc & 0x800) > 0) tb = 1; else tb = 0;
ecc = (ecc << 1) & 0xfff;
if (tb != c) nbb++;
}
}
}
/************************************************************************/
/* proc_frame */
/* */
/* This routine processes the two raw data frames stored in array ob[]. */
/* Each data frame is repeated three times with the middle repetition */
/* inverted. So bits at offsets of 0, 40, and 80 should all be carrying */
/* the same information - either 010 or 101 if no errors have occured). */
/* Error correction is done by ignoring any single wayward bit in each */
/* triplet. For example a 000 triplet is assumed to have actually been */
/* a 010; a 001 -> 101; 011 -> 010; et cetera. Array tal[] holds a table*/
/* giving the "corrected" bit value for every possible triplet. Two */
/* or three wrong bits in a triplet cannot be corrected. The resulting */
/* data bits are send on the proc_cmd routine for the remaining */
/* processing. */
/************************************************************************/
void proc_frame()
{
static int i,l;
static int tal[9]={0,1,0,0,1,1,0,1};
/* do the first data frame */ proc_cmd(-1); /* reset proc_cmd routine
*/ for (i=0; i<40; i++) { l = (int) ( (ob[i]<<2) + (ob[i+40]<<1) +
ob[i+80]); /* form triplet */ l = tal[l]; /* look up the correct
bit value in the table */ proc_cmd(l); /* send out bit */ }
/* do the second data frame */
proc_cmd(-2);
for (i=0; i<40; i++)
{
l = (int) ( (ob[i+120]<<2) + (ob[i+160]<<1) + ob[i+200]);
l = tal[l];
proc_cmd(l);
}
}
/************************************************************************/
/* frame_sync */
/* */
/* This routine takes the raw bit stream and tries to find the 48 bit */
/* frame sync sequence. When found it stores the next 240 raw data bits */
/* that make up a data frame and stores them in global array ob[]. */
/* Routine proc_frame is then called to process the data frame and the */
/* cycle starts again. When mode = 1 it will only look for the dotting */
/* sequence and updat the dotting flag. */
/************************************************************************/
void frame_sync(char gin)
{
static int sr0=0,sr1=0,sr2=0,hof = 300,xr0,xr1,xr2,i,nh=0,ninv=0;
static int fsy[3][3]={
0x1555,0x5712,0x5555, /* control channel frame sync */
0x5555,0x5555,0x5555, /* dotting sequence */
0xAAAA,0xAAAA,0x85D3 /* data channel frame sync (not used) */
};
/* update registers holding 48 bits */
sr0 = sr0 << 1;
if ( (sr1 & 0x8000) != 0) sr0 = sr0 ^ 0x01;
sr1 = sr1 << 1;
if ( (sr2 & 0x8000) != 0) sr1 = sr1 ^ 0x01;
sr2 = sr2 << 1;
sr2 = sr2 ^ (int) gin;
/* update ob array */
if (obp <600)
{
ob[obp] = gin;
obp++;
}
/* find number of bits not matching sync pattern */
xr0 = sr0 ^ fsy[mode][0];
xr1 = sr1 ^ fsy[mode][1];
xr2 = sr2 ^ fsy[mode][2];
nh = 0;
for (i=0; i<16; i++)
{
if ( (xr0 & 0x01) > 0) nh++; xr0 = xr0 >> 1;
if ( (xr1 & 0x01) > 0) nh++; xr1 = xr1 >> 1;
if ( (xr2 & 0x01) > 0) nh++; xr2 = xr2 >> 1;
}
/* if there are less than 3 mismatches with sync pattern and we aren't
*/ /* inside a data frame (hold-off counter less than 288) sync up */
if ( nh < 4) { /* mode zero means we're monitoring control channel */
if ( (hof > 287) && (mode == 0) ) { proc_frame(); obp = 0; hof = 0;
ninv = 0; } /* mode 1 means we're on voice channel and so we just found
dotting */ else if ((mode == 1) && (nh < 2) ) { dotting++; } }
/* check for polarity inversion - if all frame sync bits mismatch 12 */
/* times in a row without ever getting a good match assume that one */
/* must invert the bits */
if ((nh == 48) && (mode == 0))
{
ninv++;
if (ninv > 12)
{
invfla ^= 0x01;
gotoxy (65,1);
if (invfla == 1) cprintf("INVERT"); else cprintf(" ");
ninv = 0;
}
}
if (hof < 1000) hof++;
}
/************************************************************************/
/* DISPLAY HELP SCREEN */
/************************************************************************/
void help()
{
printf("\n EDACS control channel monitoring program\n");
printf(" Command line arguement summary\n\n");
printf(" /NC:x - set x to number of channels used in system you \n");
printf(" are monitoring. Minimum value = 3; max = 31 \n");
printf(" /COM:y - set y = 1,2,3,4 to set com port you want to use.\n");
printf("\nExample: if your program is called edacs.exe and you wish to \n");
printf(" monitor a 10 channel system using COM2 you should type \n");
printf(" the following in at the DOS prompt:\n\n");
printf(" EDACS /nc:10 /com:2\n");
printf("\n Interface requirements: Hamcomm type data slicer circuit.\n");
printf(" The program automatically determines the property polarity of \n");
printf(" the incoming data. When everything is working properly you will\n");
printf(" see the the character on the upper right hand corner of your \n");
printf(" screen spinning around indicating that EDACS control channel \n");
printf(" information is being successfully processed.\n\n");
printf(" See text file for further details...\n\n");
printf(" press any key to continue...\n");
getch();
}
void main (int argc,char *argv[],char *env[])
{
static unsigned int n,i=0,j;
static int irqv = 0x0c,ef=0,ch;
FILE *out;
static char s=48,temp[20];
static double dt,exc=0.0,clk=0.0,xct,dto2;
for (n=1; n<argc; n++)
{
strcpy(temp,argv[n]);
strupr(temp);
j+=sscanf(temp,"/NC:%i",&nucha);
j+=sscanf(temp,"/COM:%i",&sport);
if (temp[1] == 'H') j=30;
}
if (nucha >31) { nucha = 31; j = 30; }
if (nucha < 3) { nucha = 3; j = 30; }
if (sport > 4) { sport = 4; j = 30; }
if (sport < 1) { sport = 1; j = 30; }
if ( (j+1) != argc) help();
clrscr();
/* dt is the number of expected clock ticks per bit */
dt = 1.0/(9600.0*838.22e-9);
dto2 = 0.5*dt;
/* set up items related to serial port */
n = inportb (0x21);
if ( (sport == 1) | (sport == 3))
{
irqv = 0x0c;
oldfuncc = getvect(irqv); /* save COM Vector */
setvect (irqv, comint); /* Capture COM vector */
outportb(0x21, n & 0xef);
if (sport == 1) comport = 0x3f8; else comport = 0x3e8;
}
else
{
irqv = 0x0b;
oldfuncc = getvect(irqv); /* save COM Vector */
setvect (irqv, comint); /* Capture COM vector */
outportb(0x21, n & 0xf7);
if (sport == 2) comport = 0x2f8; else comport = 0x2e8;
}
set8253(); /* set up 8253 timer chip */
set8250(baudrate); /* set up 8250 UART */
printf("Checking for data coming in on COM%i... \n",sport);
printf("If program just sits here press any key to exit...\n");
while ( (cpstn < 3) & (kbhit() == 0) );
if (cpstn < 3) {
printf("HEY - no data seems to be coming in over your interface.\n\n");
outportb (0x21, n); /* disable IRQ interrupt */
setvect (irqv, oldfuncc); /* restore old COM Vector */
exit(1);
} else
printf("Interface seems to work properly...\n\n");
if (nucha >31) nucha = 31;
if (nucha < 3) nucha = 3;
show_setup();
while (ef == 0)
{
/* check if key has been hit */
if (kbhit() != 0)
{
ch = getch();
/* a space or Esc key press sets the exit flag */
if ((ch == 32) | (ch == 27)) ef = 1;
else
{
/* any other key press returns the scanner to the control */
/* channel if it had switched to an active channel */
set_freak(chan[control]);
mode = 0;
i = cpstn;
}
}
if (i != cpstn)
{
s = (char) ((fdata[i] >> 15) ^ invfla );
/* add in new number of cycles to clock */ clk += (fdata[i] &
0x7fff); xct = exc + dto2; /* exc is current boundary */ while ( clk
>= xct ) { frame_sync(s); clk = clk - dt; } /* clk now holds new
boundary position. update exc slowly... */ /* 0.005 sucks; 0.02 better;
0.06 mayber even better; 0.05 seems pretty good */ exc = exc +
0.050*(clk - exc);
i++;
if( i >buflen) i = 0;
/* If we are sitting on an active channel and the dotting sequence */
/* is detected, we should switch back to the control channel */
if ( (mode == 1) && (dotting > 0))
{
set_freak(chan[control]);
mode = 0;
dotting = 0;
i = cpstn;
}
}
}
outportb (0x21, n); /* disable IRQ interrupt */
setvect (irqv, oldfuncc); /* restore old COM Vector */
gotoxy(1,23);
printf("\n");
}